/*
 * Proxy.cpp
 *
 *  Created on: 2013. 8. 5.
 *      Author: jjaehuny
 */

#include "../Proxy.h"
#include "CacheServer.h"
#include "LocationServer.h"
#include "GuildServer.h"
#include "../helper.h"

#include "../NLogic/Space.h"
#include "../NLogic/Equip.h"
#include "../NLogic/BackEndServerInfo.h"
#include "../NLogic/SocialInfo.h"
//#include "../NLogic/Level.h"
#include "../Enum.h"
#include "../system/config.h"
#include "../Config.h"

#include "../User.h"

#include <string>

namespace ProjectZ { namespace NProxy {

bool GetConnectionInfoFromDB()
{
	::Database::Command::Auto clsAuto(::Database::GETCOMMANDPOOL());
	clsAuto->SetQueryType(::Database::Command::S_PROCEDURE);
	clsAuto->MakeQuery("SELECT * FROM %s WHERE active = 1", ::ProjectZ::Config::Instance().GetResourceDatabase()._resource_proxy.c_str());
	if (false == ::Database::GETSESSION()->SyncExecute(1, clsAuto))
		return false;

	if( clsAuto->GetErrorIdx() != 0 )
		return false;

	if (0 == clsAuto->ResultRowCount())
		return false;

	int seq = 0;
	int active = 0;
	int type = 0;
	int idx = 0;
	::Toolkit::Str<20> ip;
	int port = 0;

	while( clsAuto->Next() )
	{
		clsAuto->GetRecord() >> seq >> active >> type >> idx >> ip >> port;
		PROCESSLOG("GetConnectionInfoFromDB CONNECT IP : %s PORT : %d", ip.c_str(), port);
		if (false == Connect(static_cast<ProxyType::ENUM>(type), idx, ip.c_str(), port))
		{
			ERRORLOG("GetConnectionInfoFromDB CONNECT FAIL IP : %s PORT : %d", ip.c_str(), port);
			return false;
		}
	}

	return true;
}

bool Connect(ProxyType::ENUM eType, int idx, std::string strIP, int port) {
	bool bRet = false;
	if (ProxyType::CACHESERVER == eType) {
		CacheServer * pCacheServer = new CacheServer();
		bRet = pCacheServer->Connect(idx, strIP, port);

		if (bRet == true) {
			s_CacheTree[idx] = pCacheServer;
		}
	} else if (ProxyType::LOCATIONSERVER == eType) {
		LocationServer * pLocationServer = new LocationServer();
		bRet = pLocationServer->Connect(idx, strIP, port);

		if (bRet == true) {
			s_LocationTree[idx] = pLocationServer;
		}
	} else if (ProxyType::FRIENDSSERVER == eType) {
		FriendsServer * pFriednsServer = new FriendsServer();
		bRet = pFriednsServer->Connect(idx, strIP, port);

		if (bRet == true) {
			s_FriendsTree[idx] = pFriednsServer;
		}
	} else if (ProxyType::GUILDSERVER == eType) {
		GuildServer * pGuildServer = new GuildServer();
		bRet = pGuildServer->Connect(idx, strIP, port);

		if (bRet == true) {
			s_GuildTree[idx] = pGuildServer;
		}
	}

	return true;
}

void Finalize()
{
	std::for_each(s_CacheTree.begin(), s_CacheTree.end(), NUtil::del_second_ptr<  std::map<int, CacheServer*>::value_type  >);
	std::for_each(s_LocationTree.begin(), s_LocationTree.end(), NUtil::del_second_ptr<  std::map<int, LocationServer*>::value_type  >);
	std::for_each(s_FriendsTree.begin(), s_FriendsTree.end(), NUtil::del_second_ptr<  std::map<int, FriendsServer*>::value_type  >);
	std::for_each(s_GuildTree.begin(), s_GuildTree.end(), NUtil::del_second_ptr<  std::map<int, GuildServer*>::value_type  >);

	std::for_each(s_UserTree.begin(), s_UserTree.end(), [](std::map<int, User *>::value_type& v) {
		User::Factory::Instance().DEL( v.second );
	} );
	s_UserTree.clear();
}

// Proxy 를 사용하기 전 유저 접속이 일어나면 호출이 되어야 한다.
bool Initial(User * pUser, int user_seq) {
	auto itr = s_UserTree.find(user_seq);
	if (itr != s_UserTree.end())
		return false;

	s_UserTree[user_seq] = pUser;

	return true;
}

// Proxy 를 사용후 유저 접속이 종료된 경우 호출 되어야 한다.
void Final(User * pUser) {

	STRESSLOG("FINAL SOCIALID : %s GID : %d", pUser->GetSocialID().c_str(), pUser->GetUserSeq());

	auto itr = s_UserTree.find(pUser->GetUserSeq());
	if (itr == s_UserTree.end())
		return;

	s_UserTree.erase(itr);

	RemoveUser(pUser);
	RemoveFRUserSyn(pUser);
}

void RemoveUser(int user_seq)
{
	auto itr = s_UserTree.find(user_seq);
	if (itr == s_UserTree.end())
	{
		DEBUGLOG("%s NOT FOUND USER", __FUNCTION__);
		return;
	}

	s_UserTree.erase(itr);
}

User * GetUser(int user_seq) {
	std::map<int, User *>::const_iterator iter = s_UserTree.find(user_seq);

	if (iter == s_UserTree.end())
		return NULL;
	else
		return iter->second;
}

const std::map<int, User *>& GetUserTree() {
	return s_UserTree;
}

bool UserInfoSyn(User * pUser) {
//	if (0 >= pUser->GetUserSeq())
//		return false;

//	CacheServer * pCacheServer = GetCacheServer(pUser->GetUserSeq());

//	if (pUser->)

	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return false;
	}

	::UserInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	pCacheServer->SendMessage(msg);

	return true;
}

bool CharacterInfoSyn(User * pUser, int char_seq) {
//	if (0 >= pUser->GetUserSeq())
//		return false;
//
//	CacheServer * pCacheServer = GetCacheServer(pUser->GetUserSeq());
	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return false;
	}

	DEBUGLOG("[CHANNEL] CharacterInfoSyn USER SEQ : %d", pUser->GetUserSeq());
	::CharacterInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	msg.char_seq = char_seq;

	pCacheServer->SendMessage(msg);

	return true;
}

bool NewCharacterInfoSyn(User * pUser, int char_seq)
{
//	if (0 >= pUser->GetUserSeq())
//		return false;
//
//	CacheServer * pCacheServer = GetCacheServer(pUser->GetUserSeq());
	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return false;
	}

	DEBUGLOG("[CHANNEL] CharacterInfoSyn USER SEQ : %d", pUser->GetUserSeq());
	::NewCharacterInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	msg.char_seq = char_seq;

	pCacheServer->SendMessage(msg);

	return true;
}

bool FlushCharacterInfoSyn(User * pUser, int char_seq, bool flushDB) {
//	if (0 >= pUser->GetUserSeq())
//		return false;
//
//	CacheServer * pCacheServer = GetCacheServer(pUser->GetUserSeq());
	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return false;
	}

	pUser->PreFlush();

	// exp update
//	pUser->GetCharacterInfo().level = pUser->GetLevel()->GetUserLevel();
//	pUser->GetCharacterInfo().exp = pUser->GetLevel()->GetUserExp();

//	pUser->GetUserInfo().array_Slot[pUser->GetCharacterInfo().slotindex].level = pUser->GetCharacterInfo().level;
//
//	// equip update
//	pUser->GetEquip()->UpdateCharacterInfo();
//
//	{
//		std::stringstream stream;
//		for (int i = 0; i < MAX_DUNGEON_COUNT; i++)
//		{
//			stream << pUser->Get_array_dungeon_clear_info()[i];
//			if ( i < MAX_DUNGEON_COUNT - 1)
//				stream << ",";
//		}
//		pUser->GetCharacterInfo().dungeon_clear_info = stream.str().c_str();
//	}
//	{
//		std::stringstream stream;
//		for (int i = 0; i < BAG_TYPE_MAX; i++)
//		{
//			stream << pUser->Get_array_bag_order_info()[i];
//			if ( i < BAG_TYPE_MAX - 1)
//				stream << ",";
//		}
//		pUser->GetCharacterInfo().bag_order_info = stream.str().c_str();
//	}


	::FlushCharacterInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	msg.char_seq = char_seq;
	msg.flushDB = flushDB;
	msg.stCharacterInfo = pUser->GetCharacterInfo();

	pCacheServer->SendMessage(msg);
	//DEBUGLOG("TRACE chraceinfo size : %d GID : %d", msg.Size(), pUser->GetUserSeq());

	return true;
}

bool FlushNewCharacterInfoSyn(User * pUser, int char_seq, int slotindex, bool flushDB)
{
	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return false;
	}

	::FlushCharacterInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	msg.char_seq = char_seq;
	msg.flushDB = flushDB;
	msg.stCharacterInfo = pUser->GetCharacterInfoFromIndex(slotindex);

	pCacheServer->SendMessage(msg);

	//DEBUGLOG("TRACE chraceinfo size : %d", msg.Size());

	return true;
}

bool FlushUserInfoSyn(User * pUser, bool flushDB) {
//	if (0 >= pUser->GetUserSeq())
//		return false;
//
//	CacheServer * pCacheServer = GetCacheServer(pUser->GetUserSeq());
	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return false;
	}

	::FlushUserInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	msg.flushDB = flushDB;
	msg.stUserInfo = pUser->GetUserInfo();

	pCacheServer->SendMessage(msg);

	return true;
}

bool FlushUserSlotInfoSyn(User * pUser, int slotIndex) {
//	if (0 >= pUser->GetUserSeq())
//		return false;
//
//	CacheServer * pCacheServer = GetCacheServer(pUser->GetUserSeq());
	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return false;
	}

	::FlushUserSlotInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	msg.slotIndex = slotIndex;
	msg.stSlot = pUser->GetUserInfo().array_Slot[slotIndex];

	pCacheServer->SendMessage(msg);

	return true;
}

void RemoveUser(User * pUser) {
//	if (0 >= pUser->GetUserSeq())
//		return;
//
//	CacheServer * pCacheServer = GetCacheServer(pUser->GetUserSeq());
	CacheServer * pCacheServer = GetCacheServer(pUser);
	if (NULL == pCacheServer) {
		return;
	}

	::RemoveUserSyn msg;
	msg.seq = pUser->GetUserSeq();

	pCacheServer->SendMessage(msg);
}

bool RegistUser(User * pUser) {
//	if (0 >= pUser->GetUserSeq())
//		return false;
//
//	LocationServer * pLocationServer = GetLocationServer(pUser->GetUserSeq());
	LocationServer * pLocationServer = GetLocationServer(pUser);
	if (NULL == pLocationServer) {
		DEBUGLOG("[LOCATION RegistUser] no location server, seq: %d", pUser->GetUserSeq());
		return false;
	}

	RegistSyn msg;
	msg.seq = pUser->GetUserSeq();

	pLocationServer->SendMessage(msg);
	DEBUGLOG("[LOCATION RegistUser] SEQ : %d", pUser->GetUserSeq());

	return true;
}

bool UnRegistUser(User * pUser) {
//	if (0 >= pUser->GetUserSeq())
//		return false;
//
//	LocationServer * pLocationServer = GetLocationServer(pUser->GetUserSeq());
	LocationServer * pLocationServer = GetLocationServer(pUser);
	if (NULL == pLocationServer) {
		return false;
	}

	UnRegistSyn msg;
	msg.seq = pUser->GetUserSeq();

	pLocationServer->SendMessage(msg);

	return true;
}

bool UnRegistUser(uint32_t seq) {
	LocationServer * pLocationServer = GetLocationServer(seq);
	if (NULL == pLocationServer) {
		DEBUGLOG("UnRegistUser(uint32_t seq) NULL == pLocationServer SEQ : %d", seq);
		return false;
	}

	UnRegistSyn msg;
	msg.seq = seq;

	pLocationServer->SendMessage(msg);

	return true;
}

bool UnRegistUser(int userseq) {
	LocationServer * pLocationServer = GetLocationServer(userseq);
	if (NULL == pLocationServer) {
		return false;
	}

	UnRegistSyn msg;
	msg.seq = userseq;

	pLocationServer->SendMessage(msg);

	return true;
}

bool Chat(User * pUser, int type, int receiver_seq, std::string message) {
	LocationServer * pLocationServer = GetLocationServer(receiver_seq);
	//LocationServer * pLocationServer = GetLocationServer(pUser);
	if (NULL == pLocationServer) {
		return false;
	}

	ChatSyn msg;
	msg.type = type;
	msg.sender_seq = pUser->GetUserSeq();
	msg.receiver_seq = receiver_seq;
	msg.sender_nick = pUser->GetUserInfo().nickname.c_str();
	msg.msgs = message;

	pLocationServer->SendMessage(msg);

	return true;
}

bool Recall(User * pUser, int receiver_seq) {
//	LocationServer * pLocationServer = GetLocationServer(receiver_seq);
	LocationServer * pLocationServer = GetLocationServer(pUser);
	if (NULL == pLocationServer) {
		return false;
	}

	RecallSyn msg;
	msg.sender_seq = pUser->GetUserSeq();
	msg.receiver_seq = receiver_seq;
	msg.ip = ::ProjectZ::Config::Instance().GetResourceFile()._myIP;
	msg.port = ::ProjectZ::Config::Instance().GetResourceDatabase()._port;
	msg.server_id = ::ProjectZ::Config::Instance().GetResourceDatabase()._channelUniqueIndex;
	msg.channel_id = pUser->GetSpace()->GetData()._index;

	pLocationServer->SendMessage(msg);

	return true;
}

bool RecallAck(uint32_t user_seq, uint32_t receiver_seq, uint16_t is_accept) {
	LocationServer * pLocationServer = GetLocationServer(receiver_seq);
	if (NULL == pLocationServer) {
		return false;
	}

	::RecallAck msg;
	msg.sender_seq = user_seq;
	msg.receiver_seq = receiver_seq;
	msg.is_accept = is_accept;

	pLocationServer->SendMessage(msg);

	return true;
}

bool SetMainCharacterInfo(User * pUser)
{
//	LocationServer * pLocationServer = GetLocationServer(pUser->GetUserSeq());
	LocationServer * pLocationServer = GetLocationServer(pUser);
	if (NULL == pLocationServer) {
		return false;
	}

	if (pUser->GetUserInfo().main_slot_index > 7 || 0 > pUser->GetUserInfo().main_slot_index)
	{
		DEBUGLOG("ERROR GID : %d mainslotindex : %d", pUser->GetUserSeq(), pUser->GetUserInfo().main_slot_index);
		return false;
	}

	if(time(NULL) < pUser->GetCharacterInfoSetTime() + (60))
	{
		return true;
	}
	else
		pUser->SetCharacterInfoSetTime(time(NULL));

	NLogic::Equip * pEquip = pUser->GetEquip();
	if (NULL == pEquip)
	{
		DEBUGLOG("%s pEquip is NULL GID : %d", __FUNCTION__, pUser->GetUserSeq());
		return false;
	}

	SetMainCharacterInfoSyn msg;
	msg.seq = pUser->GetUserSeq();
	msg.level = pUser->GetCharacterInfo().level;
	msg.class_type = pUser->GetCharacterInfo().classtype;

	// 아바타를 장착한 경우
	if (true == pEquip->IsExistItem(ITEM_EQUIP_POS_AVARTAR))
	{
		msg.head	= pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_AVARTAR) + 100;
		msg.armor	= pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_AVARTAR) + 100;
		msg.weapon	= pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_AVARTAR) + 100;
		msg.side_hand = pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_AVARTAR) + 100;
	}
	else
	{
		msg.head	= pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_HELMET);
		msg.armor	= pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_ARMOR);
		msg.weapon	= pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_WEAPON);
		msg.side_hand = pEquip->GetEquipItemIconIndex(ITEM_EQUIP_POS_WEAPON);
	}

	msg.effect = 0;
	NLogic::Item * pItem = pEquip->GetItem(ITEM_EQUIP_POS_PET);
	if (pItem == NULL)
		msg.pet_tid = -1;
	else
		msg.pet_tid = pItem->GetData().tid;

	NLogic::SocialInfo::Data* pSocialInfoData = pUser->GetSocialInfo()->GetData();
	if ( NULL != pSocialInfoData )
	{
		msg.profile_opened = pSocialInfoData->_profile_opened;
		msg.gender = pSocialInfoData->_gender;
		msg.gender_open = pSocialInfoData->_isGenderOpen;
		msg.birthday_open = pSocialInfoData->_isBirthdayOpen;
		msg.strBirthday = pSocialInfoData->_birthday.c_str();
		// TODO 새롭게 추가되어야 한다.
	}

	pLocationServer->SendMessage(msg);

	return true;
}

bool GetMainCharacterInfo(User * pUser, int target_seq)
{
	LocationServer * pLocationServer = GetLocationServer(target_seq);
	//LocationServer * pLocationServer = GetLocationServer(pUser);
	if (NULL == pLocationServer) {
		return false;
	}

	GetMainCharacterInfoSyn msg;

	msg.seq = pUser->GetUserSeq();
	msg.target_seq = target_seq;

	pLocationServer->SendMessage(msg);

	return true;
}

bool GetMainCharacterInfo_V2(User * pUser, int target_seq)
{
	LocationServer * pLocationServer = GetLocationServer(target_seq);
	//LocationServer * pLocationServer = GetLocationServer(pUser);
	if (NULL == pLocationServer) {
		return false;
	}

	GetMainCharacterInfoSyn_V2 msg;

	msg.seq = pUser->GetUserSeq();
	msg.target_seq = target_seq;

	pLocationServer->SendMessage(msg);

	return true;
}

//CacheServer* GetCacheServer(int user_seq) {
//	int mod = user_seq % s_CacheTree.size();
//	std::map<int, CacheServer *>::const_iterator iter = s_CacheTree.find(mod);
//	if (iter != s_CacheTree.end())
//		return iter->second;
//	else
//		return NULL;
//}

CacheServer * GetCacheServer(User * pUser)
{
	NLogic::BackEndServerInfo * pBackEndServerInfo = pUser->GetBackEndServerInfo();
	if (NULL == pBackEndServerInfo){
		DEBUGLOG("ERROR> GetCacheServer()> 10. pBackEndServerInfo is NULL.> USER SEQ : %d", pUser->GetUserSeq());
		return NULL;
	}

	if (0 >= pUser->GetUserSeq()){
		DEBUGLOG("ERROR> GetCacheServer()> 20. Invalid UserSeq> USER SEQ : %d", pUser->GetUserSeq());
		return NULL;
	}


	//--------------------------------------------------------------------------------------------------------------------------------
	// cache 서버 저장에 실패할 case 가정, Changed by spencer(2014.06.18)

	// '_cacheServerIndex' 값이 user'seq % 10 한 값과 틀리다고 하면 최종적으로 cache 저장에 실패하게 된다. 즉 값의 범위는 valid하나 정확도는 invalid하다.
	// 원인은 정확히 모르지만 잘못된 값일 수도 있다.
	// 0~9 사이의 임의 cache 서버로 메시지를 전달한다.
	// 그런데 그 cache 서버에서 메시지를 받은 후 동일한 mode 연산을 수행해서 user를 검색하는데 검색에 실패하게 되어 최종 저장이 실패하게 된다.
	// (cache서버에서 정보 저장에 실패하는 첫번째 이유는 user 정보 find 실패이다.)

	// => 정확한 cache 서버 정보를 get하기 위해서 (cache 서버와 동일하게) mode 연산을 수행한다. (이전 code와는 mode 연산 추가된 것만 차이가 난다.)
	//--------------------------------------------------------------------------------------------------------------------------------

	int iCacheTreeSize = static_cast<int>(s_CacheTree.size());
	// 단순히 아래와 같이 0~9 사이값이라고 valid가 보장되지는 않는다.
	//if (pBackEndServerInfo->GetData()._cacheServerIndex < 0 )
	//if (pBackEndServerInfo->GetData()._cacheServerIndex < 0 || iCacheTreeSize <= pBackEndServerInfo->GetData()._cacheServerIndex)
	{
		int mod = pUser->GetUserSeq() % iCacheTreeSize;//s_CacheTree.size();

		if (0 <= pBackEndServerInfo->GetData()._cacheServerIndex && mod != pBackEndServerInfo->GetData()._cacheServerIndex )
		{ // 이런 경우가 잘못된 cache 서버를 선택하게 되어, 최종적으로 저장 실패를 야기하게 되는 것이다.
			ERRORLOG("ERROR> GetCacheServer()> 99. Invalid cacheServerIndex> USER SEQ : %d, CacheTree size: %d, mod: %d, _cacheServerIndex: %d"
					, pUser->GetUserSeq(), iCacheTreeSize, mod, pBackEndServerInfo->GetData()._cacheServerIndex);
		}


		std::map<int, CacheServer *>::const_iterator iter = s_CacheTree.find(mod);
		if (iter == s_CacheTree.end()){
			DEBUGLOG("ERROR> GetCacheServer()> 30. Not found a cache server.> USER SEQ : %d, CacheTree size: %d, mod: %d"
					, pUser->GetUserSeq(), iCacheTreeSize, mod);
			return NULL;
		}

		pBackEndServerInfo->GetData()._cacheServerIndex = iter->first; // 결과적으로 _cacheServerIndex가 사용되지 않는다.

		return iter->second;
	}

//	std::map<int, CacheServer *>::const_iterator iter = s_CacheTree.find(pBackEndServerInfo->GetData()._cacheServerIndex);
//	if (iter != s_CacheTree.end())
//		return iter->second;
//	else
//	{
//		DEBUGLOG("ERROR> GetCacheServer()> 40. Not found a cache server.> USER SEQ : %d, cacheServerIndex: %d"
//				, pUser->GetUserSeq(), pBackEndServerInfo->GetData()._cacheServerIndex);
//		return NULL;
//	}
}

LocationServer* GetLocationServer(int user_seq) {
	int mod = user_seq % s_LocationTree.size();
	std::map<int, LocationServer *>::const_iterator iter = s_LocationTree.find(mod);
	if (iter != s_LocationTree.end())
		return iter->second;
	else
		return NULL;
}

LocationServer* GetLocationServer(User * pUser)
{
	NLogic::BackEndServerInfo * pBackEndServerInfo = pUser->GetBackEndServerInfo();
	if (NULL == pBackEndServerInfo)
		return NULL;

	if (0 >= pUser->GetUserSeq())
			return NULL;

	// 잘못된 location 서버 선택에 대한 보완, Changed by spencer(2014.06.18)

	//if (pBackEndServerInfo->GetData()._locationServerIndex < 0)
	{
		int mod = pUser->GetUserSeq() % s_LocationTree.size();
		std::map<int, LocationServer *>::const_iterator iter = s_LocationTree.find(mod);
		if (iter == s_LocationTree.end())
			return NULL;

		pBackEndServerInfo->GetData()._locationServerIndex = iter->first;

		return iter->second;
	}

//	std::map<int, LocationServer *>::const_iterator iter = s_LocationTree.find(pBackEndServerInfo->GetData()._locationServerIndex);
//	if (iter != s_LocationTree.end())
//		return iter->second;
//	else
//		return NULL;
}

GuildServer * GetGuildServer(User * pUser)
{
	NLogic::BackEndServerInfo * pBackEndServerInfo = pUser->GetBackEndServerInfo();
	if (NULL == pBackEndServerInfo)
	{
		DEBUGLOG("%s : pBackEndServerInfo is NULL", __FUNCTION__);
		return NULL;
	}

	DEBUGLOG( "%s> guildTree size: %d", __FUNCTION__, s_GuildTree.size() );

	int mod = pUser->GetUserInfo().guild_seq % s_GuildTree.size();
	auto iter = s_GuildTree.find(mod);
	if (iter == s_GuildTree.end())
	{
		DEBUGLOG("%s : Not Found GuildServer", __FUNCTION__);
		return NULL;
	}

	if (false == iter->second->IsConnect())
	{
		DEBUGLOG("%s : Not Connect GuildServer", __FUNCTION__);
		return NULL;
	}

	pBackEndServerInfo->GetData()._guildServerIndex = iter->first;

	return iter->second;
}


// Friends Server
bool FlushFRUserInfoSyn(User * pUser) {
	if (0 >= pUser->GetUserSeq())
		return false;

	::FlushFRUserInfoSyn msg;
	::ProjectZ::NProxy::SendFriendsServer(pUser->GetUserSeq(), msg);

	return true;
}

void RemoveFRUserSyn(User * pUser) {
	if (0 >= pUser->GetUserSeq())
		return;

	::RemoveFRUserSyn msg;
	::ProjectZ::NProxy::SendFriendsServer(pUser->GetUserSeq(), msg);
}

FriendsServer *GetFriends(const int mod) {
	return s_FriendsTree[mod];
}

const std::map<int, FriendsServer *>& GetFriendsTree() {
	return s_FriendsTree;
}

FriendsServer* GetFriendsServer(User * pUser)
{
	NLogic::BackEndServerInfo * pBackEndServerInfo = pUser->GetBackEndServerInfo();
	if (NULL == pBackEndServerInfo)
		return NULL;

	if (0 >= pUser->GetUserSeq())
		return NULL;

	if (pBackEndServerInfo->GetData()._friendsServerIndex < 0)
	{
		int mod = pUser->GetUserSeq() % s_FriendsTree.size();
		std::map<int, FriendsServer *>::const_iterator iter = s_FriendsTree.find(mod);
		if (iter == s_FriendsTree.end())
			return NULL;

		pBackEndServerInfo->GetData()._friendsServerIndex = iter->first;
	}

	std::map<int, FriendsServer *>::const_iterator iter = s_FriendsTree.find(pBackEndServerInfo->GetData()._friendsServerIndex);
	if (iter != s_FriendsTree.end())
		return iter->second;
	else
		return NULL;
}

void TradeItemSoldout(User * pUser, int recv_userseq, uint64_t tradeseq, int classtype, int sheettype)
{
	LocationServer * pLocationServer = GetLocationServer(recv_userseq);
	if (NULL == pLocationServer)
		return;

	::TradeItemSoldoutSyn msg;
	msg.buyer_characterseq	= pUser->GetCharacterInfo().characterseq;	// 아이템 구매자의 캐릭터Seq
	msg.seller_userseq		= recv_userseq;								// 아이템 판매자
	msg.item_trade_seq		= tradeseq;
	msg.item_class_type		= classtype;
	msg.item_sheet_type		= sheettype;

	pLocationServer->SendMessage(msg);
}

void ApplyGuildPoint(User * pUser, int contents, int value)
{
	if (0 >= pUser->GetUserInfo().guild_seq)
		return;

	ApplyGuildPointSyn syn;
	syn.guild_seq	= pUser->GetUserInfo().guild_seq;
	syn.user_seq	= pUser->GetUserSeq();
	syn.contents	= contents;	// 1:던전, 2:PvP, 3:PK
	syn.value		= value;

	SendGuildServer(pUser, syn);
}

void UpdateGuildLoginTime(User * pUser)
{
	if (0 >= pUser->GetUserInfo().guild_seq)
		return;

	UpdateGuildLoginTimeSyn syn;
	syn.guild_seq	= pUser->GetUserInfo().guild_seq;
	syn.user_seq	= pUser->GetUserSeq();

	SendGuildServer(pUser, syn);
}

const std::map<int, GuildServer *>& GetGuildServerTree()
{
	return s_GuildTree;
}

} /*NProxy*/ } /*ProjectZ*/

